Skip to content

业务场景举例

场景一:秒杀/限量抢购

涉及工具: RLock + RAtomicLong

问题: 库存只有 100 件,瞬间几万请求涌入,如何保证不超卖?

思路:

1. 系统启动时:RAtomicLong stock = 100
2. 请求进来先 tryLock(非阻塞,抢不到直接返回"已售罄")
3. 拿到锁后:
   - 读取 stock.get()
   - 若 > 0 则 stock.decrementAndGet(),写订单
   - 若 = 0 直接返回售罄
4. finally 释放锁

也可以用 RSemaphore 替代,许可证数量 = 库存数量,acquire 成功即代表抢到,语义更自然。


场景二:用户登录 Session 共享

涉及工具: RMapCache

问题: 多台服务器部署,用户登录后请求可能打到不同机器,Session 不能只存本地。

思路:

1. 用户登录成功,生成 token
2. RMapCache<String, UserInfo> sessionMap
   sessionMap.put(token, userInfo, 30, TimeUnit.MINUTES)
   // 自带 TTL,30 分钟无操作自动过期
3. 每次请求拦截器中:
   - sessionMap.get(token) 取用户信息
   - 取到则 sessionMap.updateEntryExpiration() 续期
   - 取不到则跳转登录

比手写 RedisTemplate 更省心,TTL 续期、序列化都封装好了。


场景三:接口防重复提交

涉及工具: RMapCacheRBucket + RLock

问题: 用户快速点击两次"提交订单",后端收到两条相同请求,如何幂等?

思路:

1. 前端提交时携带一个幂等 key(如表单 token)
2. 后端收到请求:
   - RLock lock = redisson.getLock("idempotent:" + idempotentKey)
   - tryLock 失败 → 说明有相同请求正在处理,直接返回"请勿重复提交"
   - tryLock 成功 →
       检查 RBucket<Boolean> processed 是否已存在
       存在 → 返回上次结果
       不存在 → 执行业务,完成后 set processed=true(TTL 24h)
3. finally 释放锁

场景四:排行榜(积分榜/销量榜)

涉及工具: RScoredSortedSet

问题: 游戏积分实时排行,支持查询某用户排名、前 N 名列表。

思路:

1. 用户得分时:
   scoredSortedSet.addScore("userId_123", deltaScore)
   // 底层是 Redis ZINCRBY,原子操作无需加锁

2. 查前 10 名:
   scoredSortedSet.entryRangeReversed(0, 9)
   // 返回 ScoredEntry 列表,包含 userId 和 score

3. 查某用户排名:
   scoredSortedSet.reverseRank("userId_123")
   // 从高到低的名次,O(log N)

场景五:分布式任务调度(同一任务只跑一次)

涉及工具: RLock / RScheduledExecutorService

问题: 定时任务部署了 3 个节点,每天凌晨对账,只能跑一次,不能重复执行。

思路:

// 方案 A:用锁抢占
@Scheduled(cron = "0 0 2 * * ?")
public void reconcile() {
    RLock lock = redisson.getLock("task:reconcile:lock");
    // 抢到锁的节点执行,其他节点 tryLock 返回 false 直接跳过
    if (lock.tryLock()) {
        try {
            doReconcile();
        } finally {
            lock.unlock();
        }
    }
}

// 方案 B:用 RScheduledExecutorService
// 把任务提交给集群,由 Redisson 选一个节点执行,天然单次
RScheduledExecutorService executor = redisson.getExecutorService("task-pool");
executor.scheduleAtFixedRate(new ReconcileTask(), 0, 1, TimeUnit.DAYS);

场景六:限流(API 调用频率控制)

涉及工具: RRateLimiter

问题: 对外开放的 API,每个用户每分钟最多调用 100 次。

思路:

// 初始化(通常在用户首次调用时)
RRateLimiter limiter = redisson.getRateLimiter("ratelimit:userId:" + userId);
limiter.trySetRate(RateType.PER_CLIENT, 100, 1, RateIntervalUnit.MINUTES);

// 每次请求进来
if (!limiter.tryAcquire()) {
    throw new TooManyRequestsException("调用频率超限");
}
// 通过则正常执行业务

底层是令牌桶算法,由 Redisson 用 Lua Script + Redis 实现,天然分布式,多节点共享同一个限流器。


场景七:缓存击穿防护

涉及工具: RLock + RMapCache

问题: 热点 key 过期瞬间,大量请求同时穿透到数据库(缓存击穿)。

思路:

public UserInfo getUserInfo(Long userId) {
    String cacheKey = "user:" + userId;

    // 1. 先查缓存
    UserInfo info = cache.get(cacheKey);
    if (info != null) return info;

    // 2. 缓存未命中,用互斥锁防止击穿
    RLock lock = redisson.getLock("rebuild:" + cacheKey);
    lock.lock();
    try {
        // 3. Double Check(可能锁等待期间已被其他线程重建)
        info = cache.get(cacheKey);
        if (info != null) return info;

        // 4. 真正查数据库并回填缓存
        info = userDao.findById(userId);
        cache.put(cacheKey, info, 10, TimeUnit.MINUTES);
        return info;
    } finally {
        lock.unlock();
    }
}

整体规律总结

场景类型核心工具关键思想
库存/名额竞争RLock / RSemaphore互斥或许可证控制并发
状态共享RMapCache / RBucket替代本地缓存,加 TTL
幂等/防重RLock + RBucket锁保原子,桶记状态
排行/计数RScoredSortedSet / RAtomicLong原子更新,无需额外锁
单次任务RLock / RScheduledExecutorService抢锁即抢执行权
频率控制RRateLimiter令牌桶,开箱即用
缓存保护RLock + 双重检查用锁串行化重建过程